package com.tao.netty;

import com.tao.handler.IMSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.context.annotation.Configuration;

/**
 * @author  诸葛先生
 * 功能：netty服务端搭建
 */
@Configuration
public class IMNettyServer {
    public static void  start(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap sb = new ServerBootstrap();
            sb.option(ChannelOption.SO_BACKLOG, 1024);
            sb.group(group, bossGroup) // 绑定线程池
                    .channel(NioServerSocketChannel.class) // 指定使用的channel
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //websocket协议本身是基于http协议的，所以这边也要使用http解编码器
                            ch.pipeline().addLast(new HttpServerCodec());
                            //心跳检测
                            ch.pipeline().addLast(new IdleStateHandler(60,0,0));
                            //以块的方式来写的处理器--支持大数据流
                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            //对http消息做聚合操作，FullHttpRequest、FullHttpResponse
                            //1 http数据在传输过程是分段，HttpObjectAggregator，就是可以将多个段聚合
                            //2 当浏览器发送大量数据时，就会发出多次http请求
                            ch.pipeline().addLast(new HttpObjectAggregator(1024 * 64));
                            // webSocket 数据压缩扩展，当添加这个的时候WebSocketServerProtocolHandler的第三个参数需要设置成true
                            ch.pipeline().addLast(new WebSocketServerCompressionHandler());
/*                            //自定义入站处理(上线处理) -- 在Websocket处理器之前检测拦截请求信息
                            //1 从HTTP请求中提取所需要的数据
                            //2 安全检查
                            //3 将结果和其他数据绑定在channel
                            //4 触发安全检查完毕自定义事件
                            ch.pipeline().addLast(new HttpRequestHandler());*/
                            //1 对应websocket，它的数据是以帧(frame)的形式传递
                            //2 服务器端向外暴露的 web socket 端点，当客户端传递比较大的对象时，maxFrameSize参数的值需要调大
                            //3 错误点：WebSocketServerProtocolHandler的第二个参数要写为null
                            //4 WebSocketServerProtocolHandler 核心功能是将http协议升级为ws协议，保持长连接
                            //5 浏览器请求时：ws://localhost:8888/ws 表示请求的uri
                            // 表示请求的uri
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 10485760));
                            //自定义消息处理类--处理业务逻辑
                            ch.pipeline().addLast(new IMSocketHandler());
                        }
                    });
            ChannelFuture cf = sb.bind(port).sync(); // 服务器异步创建绑定
            System.out.println(IMNettyServer.class + "已启动，正在监听： " + cf.channel().localAddress());
            cf.channel().closeFuture().sync(); // 关闭服务器通道
        } finally {
            group.shutdownGracefully(); // 释放线程池资源
            bossGroup.shutdownGracefully();
        }
    }
}